/**
 * Copyright 1999-2014 dangdang.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.remoting.http.tomcat;


import java.io.File;
import java.util.Map;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.configure.ExtensionConfiguratorUtils;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.remoting.http.HttpHandler;
import com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet;
import com.alibaba.dubbo.remoting.http.servlet.ServletManager;
import com.alibaba.dubbo.remoting.http.servlet.StaticResourceServlet;
import com.alibaba.dubbo.remoting.http.support.AbstractHttpServer;


/**
 * @author lishen
 */
public class TomcatHttpServer extends AbstractHttpServer {

    private static final Logger logger = LoggerFactory.getLogger(TomcatHttpServer.class);

    private final Tomcat tomcat;

    private final URL url;

    @SuppressWarnings("unchecked")
	public TomcatHttpServer(URL url, final HttpHandler handler) {
        super(url, handler);

        this.url = url;
        DispatcherServlet.addHttpHandler(url.getPort(), handler);
        String baseDir = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath();
        tomcat = new Tomcat();
        tomcat.setBaseDir(baseDir);
        tomcat.setPort(url.getPort());
        Connector connector = tomcat.getConnector();
        connector.setProperty(
                "maxThreads", String.valueOf(url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS)));
//        connector.setProperty(
//                "minSpareThreads", String.valueOf(url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS)));

        connector.setProperty(
                "maxConnections", String.valueOf(url.getParameter(Constants.ACCEPTS_KEY, -1)));

        ExtensionConfiguratorUtils utils = ExtensionConfiguratorUtils.getInstance();
        if(url.getProtocol().equals("https")){
        	connector.setSecure(true);
        	connector.setScheme("https");
        	connector.setAttribute("SSLEnabled", true);
        	connector.setAttribute("sslProtocol", utils.getStringValue(new String[]{"tomcat.https.sslProtocol", "tomcat.https.ssl-protocol"}, "TLS"));
        	connector.setAttribute("ciphers", utils.getStringValue(new String[]{"tomcat.https.ciphers"}));
            connector.setAttribute("protocol", "org.apache.coyote.http11.Http11Protocol");
            connector.setAttribute("clientAuth", false);
            connector.setAttribute("keystoreFile", utils.getStringValue(new String[]{"tomcat.https.keystoreFile", "tomcat.https.keystore-file"}));
            connector.setAttribute("keystoreType", utils.getStringValue(new String[]{"tomcat.https.keystoreType", "tomcat.https.keystore-type"}, "PKCS12"));
            connector.setAttribute("keystorePass", utils.getStringValue(new String[]{"tomcat.https.keystorePassword", "tomcat.https.keystore-password"}));
            connector.setAttribute("keystoreAlias", utils.getStringValue(new String[]{"tomcat.https.keystoreAlias", "tomcat.https.keystore-alias"}));
            connector.setAttribute("keyPass", utils.getStringValue(new String[]{"tomcat.https.keyPass", "tomcat.https.key-pass", "tomcat.https.keyPassword", "tomcat.https.key-password"}));
        }
        

        connector.setProperty("URIEncoding", "UTF-8");
        connector.setProperty("connectionTimeout", "60000");

        connector.setProperty("maxKeepAliveRequests", "-1");
        connector.setProtocol("org.apache.coyote.http11.Http11NioProtocol");

        Context context = tomcat.addContext("/", baseDir);
        
        
        Object mapping = utils.getValue(new String[]{"http.servlet.mapping"});
        // virtual -> real
        if(mapping != null && mapping instanceof Map){
        	StaticResourceServlet servlet = new StaticResourceServlet();
        	Tomcat.addServlet(context, "resource", servlet);
         	Map<Object, Object> m = (Map<Object, Object>)mapping;
         	String s = File.separator;
        	for(Map.Entry<Object, Object> entry : m.entrySet()){
        		String key = entry.getKey().toString().trim();
        		String value = entry.getValue() == null?null:entry.getValue().toString().trim();
        		if(value == null || value.length() == 0) continue;
        		
        		if(!key.startsWith("/")){
        			key = "/" + key;
        		}
        		if(!key.endsWith("/")) key += "/";
        		//根目录不可以
        		if(key.equals("/") || key.equals("/dubbo/")) continue;
        		
        		if(!s.equals("/")){
        			value = value.replace("/", s);
        		}
        		if(!value.endsWith(s)) value += s;
        		
        		StaticResourceServlet.addServletMapping(key, value);
        		context.addServletMapping(key + "*", "resource");
        	}
        }
       
        Tomcat.addServlet(context, "dispatcher", new DispatcherServlet());
        context.addServletMapping("/*", "dispatcher");
        
        ServletManager.getInstance().addServletContext(url.getPort(), context.getServletContext());

        try {
            tomcat.start();
        } catch (LifecycleException e) {
            throw new IllegalStateException("Failed to start tomcat server at " + url.getAddress(), e);
        }
    }

    public void close() {
        super.close();

        ServletManager.getInstance().removeServletContext(url.getPort());

        try {
            tomcat.stop();
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }
    }
}